// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/media/peer_connection_tracker_host.h"

#include <set>
#include <utility>

#include "base/bind.h"
#include "base/no_destructor.h"
#include "base/power_monitor/power_monitor.h"
#include "base/ranges/algorithm.h"
#include "base/task/post_task.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/webrtc/webrtc_internals.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/webrtc_event_logger.h"
#include "services/service_manager/public/cpp/interface_provider.h"

namespace content {

namespace {

std::set<PeerConnectionTrackerHost*>& AllHosts() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  static base::NoDestructor<std::set<PeerConnectionTrackerHost*>> all_hosts{};
  return *all_hosts;
}

void RegisterHost(PeerConnectionTrackerHost* host) {
  AllHosts().insert(host);
}
void RemoveHost(PeerConnectionTrackerHost* host) {
  AllHosts().erase(host);
}

}  // namespace

// static
const std::set<PeerConnectionTrackerHost*>&
PeerConnectionTrackerHost::GetAllHosts() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  return AllHosts();
}

PeerConnectionTrackerHost::PeerConnectionTrackerHost(RenderFrameHost* frame)
    : frame_id_(frame->GetGlobalFrameRoutingId()),
      peer_pid_(frame->GetProcess()->GetProcess().Pid()) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  RegisterHost(this);
  base::PowerMonitor::AddPowerSuspendObserver(this);
  base::PowerMonitor::AddPowerThermalObserver(this);
  frame->GetRemoteInterfaces()->GetInterface(
      tracker_.BindNewPipeAndPassReceiver());
  // Ensure that the initial thermal state is known by the |tracker_|.
  base::PowerThermalObserver::DeviceThermalState initial_thermal_state =
      base::PowerMonitor::GetCurrentThermalState();
  if (initial_thermal_state !=
      base::PowerThermalObserver::DeviceThermalState::kUnknown) {
    OnThermalStateChange(initial_thermal_state);
  }
}

PeerConnectionTrackerHost::~PeerConnectionTrackerHost() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  RemoveHost(this);
  base::PowerMonitor::RemovePowerSuspendObserver(this);
  base::PowerMonitor::RemovePowerThermalObserver(this);
}

void PeerConnectionTrackerHost::AddPeerConnection(
    blink::mojom::PeerConnectionInfoPtr info) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
  if (webrtc_internals) {
    webrtc_internals->OnAddPeerConnection(
        frame_id_.child_id, peer_pid_, info->lid, info->url,
        info->rtc_configuration, info->constraints);
  }

  WebRtcEventLogger* logger = WebRtcEventLogger::Get();
  if (logger) {
    logger->PeerConnectionAdded(frame_id_, info->lid,
                                base::OnceCallback<void(bool)>());
  }
}

void PeerConnectionTrackerHost::RemovePeerConnection(int lid) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
  if (webrtc_internals) {
    webrtc_internals->OnRemovePeerConnection(peer_pid_, lid);
  }
  WebRtcEventLogger* logger = WebRtcEventLogger::Get();
  if (logger) {
    logger->PeerConnectionRemoved(frame_id_, lid,
                                  base::OnceCallback<void(bool)>());
  }
}

void PeerConnectionTrackerHost::UpdatePeerConnection(int lid,
                                                     const std::string& type,
                                                     const std::string& value) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  // TODO(eladalon): Get rid of magic value. https://crbug.com/810383
  if (type == "stop") {
    WebRtcEventLogger* logger = WebRtcEventLogger::Get();
    if (logger) {
      logger->PeerConnectionStopped(frame_id_, lid,
                                    base::OnceCallback<void(bool)>());
    }
  }

  WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
  if (webrtc_internals) {
    webrtc_internals->OnUpdatePeerConnection(peer_pid_, lid, type, value);
  }
}

void PeerConnectionTrackerHost::OnPeerConnectionSessionIdSet(
    int lid,
    const std::string& session_id) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  WebRtcEventLogger* logger = WebRtcEventLogger::Get();
  if (logger) {
    logger->PeerConnectionSessionIdSet(frame_id_, lid, session_id,
                                       base::OnceCallback<void(bool)>());
  }
}

void PeerConnectionTrackerHost::AddStandardStats(int lid, base::Value value) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
  if (webrtc_internals)
    webrtc_internals->OnAddStandardStats(peer_pid_, lid, std::move(value));
}

void PeerConnectionTrackerHost::AddLegacyStats(int lid, base::Value value) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
  if (webrtc_internals)
    webrtc_internals->OnAddLegacyStats(peer_pid_, lid, std::move(value));
}

void PeerConnectionTrackerHost::GetUserMedia(
    const std::string& origin,
    bool audio,
    bool video,
    const std::string& audio_constraints,
    const std::string& video_constraints) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
  if (webrtc_internals) {
    webrtc_internals->OnGetUserMedia(frame_id_.child_id, peer_pid_, origin,
                                     audio, video, audio_constraints,
                                     video_constraints);
  }
}

void PeerConnectionTrackerHost::WebRtcEventLogWrite(
    int lid,
    const std::vector<uint8_t>& output) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  std::string converted_output(output.begin(), output.end());
  WebRtcEventLogger* logger = WebRtcEventLogger::Get();
  if (logger) {
    logger->OnWebRtcEventLogWrite(
        frame_id_, lid, converted_output,
        base::OnceCallback<void(std::pair<bool, bool>)>());
  }
}

void PeerConnectionTrackerHost::OnSuspend() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  tracker_->OnSuspend();
}

void PeerConnectionTrackerHost::OnThermalStateChange(
    base::PowerThermalObserver::DeviceThermalState new_state) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  tracker_->OnThermalStateChange(
      static_cast<blink::mojom::DeviceThermalState>(new_state));
}

void PeerConnectionTrackerHost::StartEventLog(int lid, int output_period_ms) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  tracker_->StartEventLog(lid, output_period_ms);
}

void PeerConnectionTrackerHost::StopEventLog(int lid) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  tracker_->StopEventLog(lid);
}

void PeerConnectionTrackerHost::GetStandardStats() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  tracker_->GetStandardStats();
}

void PeerConnectionTrackerHost::GetLegacyStats() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  tracker_->GetLegacyStats();
}

void PeerConnectionTrackerHost::BindReceiver(
    mojo::PendingReceiver<blink::mojom::PeerConnectionTrackerHost>
        pending_receiver) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  receiver_.reset();
  receiver_.Bind(std::move(pending_receiver));
}

}  // namespace content
